#ifndef _DES_I_
#define _DES_I_

#include "DES.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
* 声明辅助函数
*/

// ShiftLeft 将28位数据循环左移
// !!函数不保证长度安全!!
static void ShiftLeft(unsigned char *arr);

// GetSubKey 根据round的值获取对应的子密钥
// 一般C,D为28字节,SubKey为48字节 即,Clen=Dlen=28,SKlen=48
static void GetSubKey(int round, unsigned char *C, unsigned char *D,
                      unsigned char *SubKey);

// SBoxChange 根据输入的48位的FR和FK,输出得到Output
static void SBoxChange(const unsigned char *FR, const unsigned char *FK,
                       unsigned char *Output);

// 获取数组中的第index位
static unsigned char GetBit(const unsigned char *Data, int Index);

// 设置数组中的第Index位为Value
static void SetBit(unsigned char *Output, int Index, unsigned char Value);

// ConvertASCToByte 将ASCII码字符串压缩成16进制字符串,若有空余则以F填充
static int ConvertASCToByte(const char *Source, int Slen, unsigned char *Dest);

// ConvertByteToASC 将16进制字符串解压成ASCII码字符串
// Dest的有效长度大于2*Slen
static int ConvertByteToASC(const unsigned char *Source, int Slen, char *Dest);

//  NOT 对输入的16字节ASCII字符串按位取反,Input是ASCII格式,16字节,Out是输出串
static int NOT(const char *Input, int Ilen, char *Out);

// EOR 对输入的16字节ASCII字符串按位异或
static int EOR(const char *LInput, int Llen, const char *RInput, int Rlen,
               char *Dest);

// DES DES(Data:16,Key:32,isEncrypt:0/1)=>Out:16
static void DES(const unsigned char *Data, const unsigned char *Key,
                int isEncrypt, unsigned char *Output);

// SingleDES SineleDES(Data:16,Key:32,isEncrypt:0/1)=>Out:16
static int SingleDES(const char *Data, const char *Key, int isEncrypt,
                     char *Out);

// TripleDES TripleDES(Data:16,Key:32,isEncrypt:0/1)=>Out:16
static int TripleDES(const char *Data, const char *Key, int isEncrypt,
                     char *Out);

/*
* 对外接口定义
*/
void test()
{
}

void SingleDESDemo()
{
    char data[] = "1112131415161718";
    char key[] = "01020304050607081111111111111111";
    char result[128] = {0};
    char eresult[128] = {0};
    printf("Data:%s\nKey:%s\n", data, key);
    if (SingleDESEncrypt(data, key, result) == SUCCESS) {
        printf("After encryption:%s\n", result);
        // result[8] = '\0';
        if (SingleDESDecrypt(result, key, eresult) == SUCCESS) {
            // eresult[8] = '\0';
            printf("After decrypted:%s\n", eresult);
        } else {
            printf("decrypted failed\n");
        }
    } else {
        printf("encryption failed\n");
    }
}
void TripleDESDemo()
{
    char data[] = "1112131415161718";
    char key[] = "01020304050607082222222222222222";
    char result[128] = {0};
    char eresult[128] = {0};
    printf("Data:%s\nKey:%s\n", data, key);
    if (TripleDESEncrypt(data, key, result) == SUCCESS) {
        printf("After encryption:%s\n", result);
        if (TripleDESDecrypt(result, key, eresult) == SUCCESS) {
            printf("After decrypted:%s\n", eresult);
        } else {
            printf("decrypted failed\n");
        }
    } else {
        printf("encryption failed\n");
    }
}

void BlocksEncrypt_3DESDemo()
{
    char data[64] = "7741004f125cd34655ea628cf62585fc";
    char key[] = "7741004f125cd34655ea628cf62585fc";
    char result[64] = {0};
    BlocksEncrypt_3DES(data, strlen(data), key, strlen(key), result);
    printf("%s\n", result);
}

void BlocksDecrypt_3DESDemo()
{
    char data[] = "5e4bfc048000c8bc9bdb786ab25d35a931cf2d65a4173a75";
    char key[] = "7741004f125cd34655ea628cf62585fc";
    char result[64] = {0};
    BlocksDecrypt_3DES(data, strlen(data), key, strlen(key), result);
    printf("%s\n", result);
}

// -----------------------
//  SingleDES 1加密 0解密
// -----------------------

// SingleDESEncrypt SingleDESEncrypt(Data:16,Key:32)=>Out:16
int SingleDESEncrypt(const char *Data, const char *Key, char *Out)
{
    return SingleDES(Data, Key, 1, Out);
}

// SingleDESDecrypt SingleDESDecrypt(Data:16,Key:32)=>Out:16
int SingleDESDecrypt(const char *Data, const char *Key, char *Out)
{
    return SingleDES(Data, Key, 0, Out);
}

// -----------------------
//  TripleDES 1加密 0解密
// -----------------------

// TripleDESEncrypt TripleDESEncrypt(Data:16,Key:32)=>Out:16
int TripleDESEncrypt(const char *Data, const char *Key, char *Out)
{
    return TripleDES(Data, Key, 1, Out);
}

// TripleDESDecrypt TripleDESDecrypt(Data:16,Key:32)=>Out:16
int TripleDESDecrypt(const char *Data, const char *Key, char *Out)
{
    return TripleDES(Data, Key, 0, Out);
}

// TAC TAC(Data:N,Value:16,Key:32):Out:8
// Value初始 16字节 "0000000000000000"
int TAC(const char *Data, int Dlen, const char *Value, int Vlen,
        const char *Key, int Klen, char *Out)
{
    char LKey[16], RKey[16], TACKey[16];
    // 拆分左右两侧
    for (int i = 0; i < 16; ++i) {
        LKey[i] = Key[i];
        RKey[i] = Key[i + 16];
    }
    // 获取异或结果
    EOR(LKey, 16, RKey, 16, TACKey);
    // 返回MAC_DES结果
    return MAC_DES(Data, Dlen, Value, Vlen, TACKey, 16, Out);
}

// BlocksEncrypt_3DES BlocksEncrypt_3DES(Data:16*n,Key:32)=>Out:16*(n+1)
int BlocksEncrypt_3DES(const char *Data, int Dlen, const char *Key, int Klen,
                       char *Out)
{
    unsigned char byLen[2] = {0, 0};
    char sTemp[16], ResultTmp[16] = {0};
    char DataIn[256] = {0};
    int LiLen = 0, LiRest = 0;

    byLen[0] = (unsigned char)Dlen / 2;
    ConvertByteToASC(byLen, 1, DataIn);
    strncat(DataIn + 2, Data, Dlen);
    LiLen = strlen(DataIn);
    LiRest = LiLen % 16;
    if (LiRest > 0) {
        strncat(DataIn + LiLen, "80", 2);
        LiRest += 2;
        for (int i = 0; i < 16 - LiRest; ++i) {
            strncat(DataIn, "0", 1);
        }
    }
    LiLen = strlen(DataIn) / 16;
    for (int i = 0; i < LiLen; ++i) {
        memcpy(sTemp, DataIn + 16 * i, 16);
        ResultTmp[0] = 0;
        TripleDES(sTemp, Key, 1, ResultTmp);
        memcpy(Out + 16 * i, ResultTmp, 16);
    }
    return SUCCESS;
}

// BlocksEncrypt_3DES BlocksEncrypt_3DES(Data:16*(n+1),Key:32)=>Out:16*n
int BlocksDecrypt_3DES(const char *Data, int Dlen, const char *Key, int Klen,
                       char *Out)
{
    unsigned char byLen[2] = {0, 0};
    char sTemp[16], ResultTmp[16] = {0};
    int count = 0;
    int LiLen = 0;
    LiLen = Dlen / 16;
    for (int i = 0; i < LiLen; ++i) {
        memcpy(sTemp, Data + 16 * i, 16);
        ResultTmp[0] = 0;
        TripleDES(sTemp, Key, 0, ResultTmp);
        memcpy(Out + 16 * i, ResultTmp, 16);
    }
    // 解密的字符串 前两位转换为10进制乘以2 则为数据的总个数
    count = ((Out[0] - '0') * 16 + Out[1] - '0') * 2;
    for (int i = 0; i < count; ++i) {
        Out[i] = Out[i + 2];
    }
    Out[count] = 0;
    return SUCCESS;
}

// MAC_DES MAC_DES(Data:N{若N%16!=0 则末尾补80...
// 至N%16=0},Value:,Key:32)=>Out:8
// Value初始 16字节 "0000000000000000"
int MAC_DES(const char *Data, int Dlen, const char *Value, int Vlen,
            const char *Key, int Klen, char *Out)
{
    char DataIn[256] = {0}, sTemp[16];
    unsigned char LbyKey[8], LbyData[8], LbyDesRes[8], LbyTemp[32];
    int LiLen = 0;
    // 设置密钥
    if (ConvertASCToByte(Key, Klen, LbyTemp) != TRUE) {
        return FAIL;
    }
    memcpy(LbyKey, LbyTemp, 8);
    // 设置初始向量
    if (ConvertASCToByte(Value, Vlen, LbyTemp) != TRUE) {
        return FAIL;
    }
    memcpy(LbyDesRes, LbyTemp, 8);
    // 整理数据块
    strncpy(DataIn, Data, Dlen);
    strncat(DataIn, "80", 2);
    LiLen = strlen(DataIn) % 16;
    // 补足尾部0
    if (LiLen > 0) {
        for (int i = 0; i < 16 - LiLen; ++i) {
            strncat(DataIn, "0", 1);
        }
    }
    LiLen = strlen(DataIn) / 16;
    // 每次取16字节的数据块进行处理
    for (int i = 0; i < LiLen; ++i) {
        // 取16字节
        memcpy(sTemp, DataIn + 16 * i, 16);
        if (ConvertASCToByte(sTemp, 16, LbyData) != TRUE) {
            return FAIL;
        }
        // 异或
        for (int j = 0; j < 8; ++j) {
            LbyData[j] = LbyData[j] ^ LbyDesRes[j];
        }
        // 加密
        DES(LbyData, LbyKey, 1, LbyDesRes);
    }
    // 取前4个十六进制,转换成8字节ASCII
    if (ConvertByteToASC(LbyDesRes, 4, Out) != TRUE) {
        return FAIL;
    }
    return SUCCESS;
}

// MAC_DES MAC_DES(Data:N{若小于16则补80...至16},Value:,Key:32)=>Out:8
// Value初始 16字节 "0000000000000000"
int MAC_3DES(const char *Data, int Dlen, const char *Value, int Vlen,
             const char *Key, int Klen, char *Out)
{
    char DataIn[256] = {0}, sTemp[16];
    unsigned char LbyKeyL[8], LbyKeyR[8], LbyData[8], LbyDesRes[8], LbyTemp[32];
    int LiLen = 0;
    // 设置密钥
    if (ConvertASCToByte(Key, Klen, LbyTemp) != TRUE) {
        return FAIL;
    }
    memcpy(LbyKeyL, LbyTemp, 8);
    memcpy(LbyKeyR, LbyTemp + 8, 8);
    // 设置初始向量
    if (ConvertASCToByte(Value, Vlen, LbyTemp) != TRUE) {
        return FAIL;
    }
    memcpy(LbyDesRes, LbyTemp, 8);
    // 拷贝数据
    strncpy(DataIn, Data, Dlen);
    strncat(DataIn, "80", 2);
    // 补零
    LiLen = strlen(DataIn) % 16;
    if (LiLen > 0) {
        for (int i = 0; i < 16 - LiLen; ++i) {
            strncat(DataIn, "0", 1);
        }
    }
    LiLen = strlen(DataIn) / 16;
    for (int i = 0; i < LiLen; ++i) {
        memcpy(sTemp, DataIn + 16 * i, 16);
        if (ConvertASCToByte(sTemp, 16, LbyData) != TRUE) {
            return FAIL;
        }
        for (int j = 0; j < 8; ++j) {
            LbyData[j] = LbyData[j] ^ LbyDesRes[j];
        }
        DES(LbyData, LbyKeyL, 1, LbyDesRes);
    }
    DES(LbyDesRes, LbyKeyR, 0, LbyDesRes);
    DES(LbyDesRes, LbyKeyL, 1, LbyDesRes);
    if (ConvertByteToASC(LbyDesRes, 4, Out) != TRUE) {
        return FAIL;
    }
    return SUCCESS;
}

// GetDeriveKey GetDeriveKey(Data:20,Key:32)=>Out:16
int GetDeriveKey(const char *Data, int Dlen, const char *Key, int Klen,
                 char *Out)
{
    // TODO
    char TmpOne[16], TmpTwo[16];
    char LKey[16], RKey[16];
    // 获取末尾16位
    for (int i = 0; i < 16; ++i) {
        TmpOne[i] = Data[Dlen - 16 + i];
    }
    memcpy(TmpOne, Data + Dlen - 16, 16);
    // 将末尾16位取反
    if (!NOT(TmpOne, 16, TmpTwo)) {
        return FAIL;
    }
    if (!(TripleDESEncrypt(TmpOne, Key, LKey) &&
          TripleDESEncrypt(TmpTwo, Key, RKey))) {
        return FAIL;
    }
    // 将两个8位密钥拼接成16位
    for (int i = 0; i < 8; ++i) {
        Out[i] = LKey[i];
        Out[i + 8] = RKey[i];
    }
    return SUCCESS;
}
// GetSessionKey GetSessionKey(Data:16,Key:32)=>Out:16
int GetSessionKey(const char *Data, int Dlen, const char *Key, int Klen,
                  char *Out)
{
    return TripleDESEncrypt(Data, Key, Out);
}

// TripleDES TripleDES(Data:16,Key:32,isEncrypt:0/1)=>Out:16
int TripleDES(const char *Data, const char *Key, int isEncrypt, char *Out)
{
    unsigned char TmpLeftKey[8], TmpRightKey[8], TmpData[8], TmpResult[8],
        TmpKey[16], TmpRecv[8];
    // 密钥从ASCII压缩成16进制的字符串
    if (ConvertASCToByte(Key, 32, TmpKey) != TRUE) {
        return FAIL;
    }
    // 分别获取左右密钥
    for (int i = 0; i < 8; ++i) {
        TmpLeftKey[i] = TmpKey[i];
        TmpRightKey[i] = TmpKey[8 + i];
    }
    // 数据从ASCII压缩成16进制的字符串
    if (ConvertASCToByte(Data, 16, TmpData) != TRUE) {
        return FAIL;
    }
    // TripleDES加密部分
    DES(TmpData, TmpKey, isEncrypt, TmpResult);
    for (int i = 0; i < 8; ++i) {
        TmpRecv[i] = TmpResult[i];
    }
    DES(TmpRecv, TmpRightKey, !isEncrypt, TmpResult);
    for (int i = 0; i < 8; ++i) {
        TmpRecv[i] = TmpResult[i];
    }
    DES(TmpRecv, TmpKey, isEncrypt, TmpResult);
    // 结果转换回ASCII
    if (ConvertByteToASC(TmpResult, 8, Out) != TRUE) {
        return FAIL;
    }
    return SUCCESS;
}

// SingleDES SineleDES(Data:16,Key:32,isEncrypt:0/1)=>Out:16
int SingleDES(const char *Data, const char *Key, int isEncrypt, char *Out)
{
    unsigned char TmpKey[16], TmpData[8], TmpResult[8];
    if (ConvertASCToByte(Key, 32, TmpKey) != TRUE) {
        return FAIL;
    }
    if (ConvertASCToByte(Data, 16, TmpData) != TRUE) {
        return FAIL;
    }
    DES(TmpData, TmpKey, isEncrypt, TmpResult);
    if (ConvertByteToASC(TmpResult, 8, Out) != TRUE) {
        return FAIL;
    }
    return SUCCESS;
}

// ShiftLeft 将28位数据循环左移
void ShiftLeft(unsigned char *arr)
{
    // 获取首位
    unsigned char tmp = arr[0];
    const int len = 28;
    // 循环左移
    for (int i = 0; i < len - 1; ++i) {
        arr[i] = arr[i + 1];
    }
    // 原首位后置
    arr[len - 1] = tmp;
}

// GetSubKey 根据round的值获取对应的子密钥
void GetSubKey(int round, unsigned char *C, unsigned char *D,
               unsigned char *SubKey)
{
    // C, D分别循环左移,移动次数取决于密钥生成移位表(ShiftTable)的内容
    for (int i = 1; i <= ShiftTable[round - 1]; ++i) {
        ShiftLeft(C);
        ShiftLeft(D);
    }
    const int SKlen = 48;
    unsigned char tmp = 0;
    // 利用置换矩阵2,生成子密钥
    for (int i = 1; i <= SKlen; ++i) {
        tmp = PC_2[i - 1];
        if (tmp <= 28)
            SubKey[i] = C[tmp - 1];
        else
            SubKey[i] = D[tmp - 28 - 1];
    }
}

// SBoxChange 根据输入的48字节的FR和FK,得到32字节的Output
void SBoxChange(const unsigned char *FR, const unsigned char *FK,
                unsigned char *Output)
{
    unsigned char Tmp[49], Result[33];
    const int InputLen = 48;
    const int OutputLen = 32;
    const int Round = 8;

    int index = 0, k = 0, Row = 0, Col = 0;
    // 扩展后异或
    for (int i = 1; i <= InputLen; ++i) {
        Tmp[i] = FR[E[i - 1]] ^ FK[i];
    }
    //根据SBoxs置换
    for (int i = 1; i <= Round; ++i) {
        index = (i - 1) * 6;
        k = (i - 1) * 4;
        // 第1位和第6位用于决定行
        Row = Tmp[index + 1] * 2 + Tmp[index + 6];
        // 第2位,第3位,第4位,第5位用于决定列
        Col = Tmp[index + 2] * 8 + Tmp[index + 3] * 4 + Tmp[index + 4] * 2 +
              Tmp[index + 5];
        for (int height = 1; height <= 4; ++height) {
            switch (height) {
                case 1:
                    Result[k + height] = (SBoxes[i - 1][Row][Col] & 8) / 8;
                    break;
                case 2:
                    Result[k + height] = (SBoxes[i - 1][Row][Col] & 4) / 4;
                    break;
                case 3:
                    Result[k + height] = (SBoxes[i - 1][Row][Col] & 2) / 2;
                    break;
                case 4:
                    Result[k + height] = (SBoxes[i - 1][Row][Col] & 1);
                    break;
            }
        }
    }
    // 获取换位后的结果
    for (int i = 1; i <= OutputLen; ++i) {
        Output[i] = Result[P[i - 1]];
    }
}

// 获取数组中的第Index位
unsigned char GetBit(const unsigned char *Data, int Index)
{
    --Index;
    // 为1返回1,为0返回0
    return (Data[Index / 8] & (128 >> (Index % 8))) > 0 ? 1 : 0;
    /*
    // 老代码部分
    if ((Data[Index / 8] & (128 >> (Index % 8))) > 0)
        return 1;
    else
        return 0;
    */
}

// 设置数组中的第Index位为Value
void SetBit(unsigned char *Output, int Index, unsigned char Value)
{
    unsigned char Bit = 128 >> (--Index % 8);
    switch (Value) {
        case 0:
            Output[Index / 8] = Output[Index / 8] & (~Bit);
            break;
        case 1:
            Output[Index / 8] = Output[Index / 8] | Bit;
            break;
    }
}

// ConvertASCToByte 将ASCII码字符串压缩,若有空余则以F填充
int ConvertASCToByte(const char *Source, int Slen, unsigned char *Dest)
{
    // 无效则返回false
    if (Slen == 0 || Source == NULL || Dest == NULL) {
        return FALSE;
    }

    unsigned char tmp;
    char CArr[2] = {'0', '\0'};
    int num = 0;
    int index = 0;

    for (int i = 0; i < Slen; ++i) {
        CArr[0] = Source[i++];
        sscanf(CArr, "%x", &num);
        tmp = num << 4;
        // 若i等于Slen结束循环
        if (i == Slen) {
            tmp |= 0x0F;
            Dest[index++] = tmp;
            break;
        }
        CArr[0] = Source[i];
        sscanf(CArr, "%x", &num);
        tmp |= num;
        Dest[index++] = tmp;
    }
    return TRUE;
}

// ConvertByteToASC 将16进制字符串解压成ASCII码字符串
// Dest的有效长度大于2*Slen
int ConvertByteToASC(const unsigned char *Source, int Slen, char *Dest)
{
    //  无效则返回false
    if (Source == NULL || Slen == 0 || Dest == NULL) {
        return FALSE;
    }
    char tmp[128];
    int index = 0;
    int single = 0;
    for (int i = 0; i < Slen; ++i) {
        sprintf(tmp, "%x%x", Source[i] >> 4, Source[i] & 0x0F);
        single = strlen(tmp);
        strncat(Dest, tmp, single);
    }
    // printf("Des:%s\n", Dest);
    return TRUE;
}

//  NOT 对输入的16字节ASCII字符串Input按位取反,Out是输出串
int NOT(const char *Input, int Ilen, char *Out)
{
    const int TmpSize = 8;
    unsigned char tmp[TmpSize];
    int result = 0;
    if (!ConvertASCToByte(Input, Ilen, tmp)) {
        return FALSE;
    }
    // 取反
    for (int i = 0; i < TmpSize; ++i) {
        tmp[i] = ~tmp[i];
    }
    // 转换
    result = ConvertByteToASC(tmp, 8, Out);

    return result;
}

// EOR 对输入的字符串按位异或,LInput,RInput均为16字节
int EOR(const char *LInput, int Llen, const char *RInput, int Rlen, char *Dest)
{
    //  无效则返回false
    if (LInput == NULL || Llen == 0 || RInput == NULL || Rlen == 0 ||
        Dest == NULL) {
        return FALSE;
    }
    unsigned char LTmp[8], RTmp[8], Result[8];
    if (!(ConvertASCToByte(LInput, Llen, LTmp) == TRUE &&
          ConvertASCToByte(RInput, Rlen, RTmp) == TRUE)) {
        return FALSE;
    }
    for (int i = 0; i < 8; ++i) {
        Result[i] = LTmp[i] ^ RTmp[i];
    }

    return ConvertByteToASC(Result, 8, Dest) == TRUE;
}

// DES DES加密算法，对用Key对Input进行加密(非0)/解密(0)
static void DES(const unsigned char *Data, const unsigned char *Key,
                int isEncrypt, unsigned char *Output)
{
    unsigned char InputValue[65], OutputValue[65], RoundKeys[17][49], L[33],
        R[33], FunctionResult[33];
    unsigned char C[29], D[29];
    const int BitLength = 64;
    const int PCLength = 28;
    const int SubKeySize = 16;
    const int RoundSize = 16;
    const int ResultSize = 32;
    // 读入输入的64位码组
    for (int i = 1; i <= BitLength; ++i) {
        InputValue[i] = GetBit(Data, i);
    }
    // 读入置换矩阵1分别按位放入C,D
    for (int i = 1; i <= PCLength; ++i) {
        C[i] = GetBit(Key, PC_1[i - 1]);
        D[i] = GetBit(Key, PC_1[i + 28 - 1]);
    }
    // 生成16个子密钥
    for (int i = 1; i <= SubKeySize; ++i) {
        GetSubKey(i, C + 1, D + 1, RoundKeys[i]);
    }
    // 把输入的64位码组分成两个32位的码组
    for (int i = 1; i <= BitLength; ++i) {
        if (i <= 32) {
            // L使用1-32
            L[i] = InputValue[IP[i - 1]];
        } else {
            // R使用1-32
            R[i - 32] = InputValue[IP[i - 1]];
        }
    }
    // 加密/解密
    for (int i = 1; i <= RoundSize; ++i) {
        if (isEncrypt) {
            // 加密过程
            SBoxChange(R, RoundKeys[i], FunctionResult);
        } else {
            // 解密过程
            SBoxChange(R, RoundKeys[17 - i], FunctionResult);
        }
        for (int j = 1; j <= ResultSize; ++j) {
            FunctionResult[j] = FunctionResult[j] ^ L[j];
        }
        for (int j = 1; j <= ResultSize; ++j) {
            L[j] = R[j];
            R[j] = FunctionResult[j];
        }
    }
    unsigned char Tmp = 0;
    for (int i = 1; i <= BitLength; ++i) {
        Tmp = InvIP[i - 1];
        if (Tmp <= 32) {
            OutputValue[i] = R[Tmp];

        } else {
            OutputValue[i] = L[Tmp - 32];
        }
    }
    for (int i = 1; i <= BitLength; ++i) {
        SetBit(Output, i, OutputValue[i]);
    }
}

#endif